
/** 
 * sun_NetCore.js
 * NET核心插件
*/
// @ts--check
/**
 * 存储复写函数时原函数的变量
 */
const sun_rmnet = {};
/**
 * 网络管理器，处理rmnet请求的静态类
 * @class
 */
class NetManager {

    /**
     * 战斗模式
     * @type {string}
     */
    static battleMode = "default";

    /**
     * 随机数模式
     * @type {string}
     */
    static _randomMode = "default";

    /**
     * 随机数种子
     * @type {number}
     */
    static _randomSeed = 114514;

    /**
     * 在线玩家人数
     * @type {number}
     */
    static _playerNum = 0;

    /**
     * 网络延迟
     * @type {number}
     */
    static _ping = 0;

    /**
     * 当前登录token
     * @type {string}
     */
    static _token = "";

    /**
     * 当前登录角色
     * @type {Game_Actor}
     */
    static _netActor = null;


    /**
     * 登陆服务器url地址
     * @type {string}
     */
    static logonUrl = "http://101.34.11.85:6543";

    /**
     * 当前登录角色起点坐标，
     * 玩家登陆后从服务器获取到玩家信息之后将当前位置临时存储到此变量中，
     * 方便后续其他方法调用
     * @type {{mapId: number, x: number, y: number}}
     */
    static _startingPoint = { "mapId": null, "x": null, "y": null };

    /**
     * 存储当前地图的其他玩家信息的变量
     * @type {Map}
     */
    static players = new Map();

    /**
     * 存储玩家的队伍信息的变量
     * @type {object}
     */
    static party = null;

    /**
     * 存储玩家所有伙伴的变量
     * @type {Map}
     */
    static parthers = null;

    /**
     * 存储玩家的物品信息的变量
     * @type {object[]}
     */
    static item = [];

    /**
     * 当前服务器在线玩家的数量
     * @readonly
     * @type {number}
     */
    static get playerNum() {
        return this._playerNum;
    }

    /**
     * 当前与服务器连接的延迟
     * @readonly
     * @type {number}
     */
    static get ping() {
        return this._ping;
    }

    /**
     * 玩家账号凭证，用于验证身份，每次登陆后更新
     * @readonly
     * @type {string}
     */
    static get token() {
        return this._token;
    }

    static set token(text) {
        this._token = text;
    }

    /**
     * rmnet中玩家的角色对象
     * @readonly
     * @type {Game_Actor}
     */
    static get actor() {
        if (this._netActor === null) {
            this._netActor = new Game_Actor(1);
        }
        return this._netActor;
    }

    /**
     * 获取角色起点坐标
     * @return {object} 起点坐标对象
     */
    static startingPoint() {
        return this._startingPoint;
    }

    /**
     * 设置场所移动后起点坐标
     * @return {object} 起点坐标对象
     */
    static setStartingPoint(mapId, x, y) {
        const point = this.startingPoint();
        point.mapId = mapId;
        point.x = x;
        point.y = y;
    }

    /**
     * 根据随机种子生成随机数，用于组队战斗等情况的数值同步
     * @returns {number} 种子随机数
     */
    static seedRandom() {
        this._randomSeed = (this._randomSeed * 9301 + 49297) % 233280;
        return this._randomSeed / 233280.0;
    }

    /**
     * rmnet创建ajax连接，该方法目前只用于角色进入地图前的登录部分
     * @private
     * @param {string} route  路由地址 
     * @param {object} data  请求数据 
     * @returns {Promise}  服务器返回的请求结果
     */
    static async _createLink(route, data) {
        const url = `${this.logonUrl}/${route}`;
        const options = {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json;charset=utf-8'
            },
            body: data
        }
        let response = await fetch(url, options);
        if (response.ok) {
            return await response.json();
        } else {
            alert("HTTP-Error: " + response.status);
        }
    }

    /**
     * 向登录服务器发送登录请求
     * @param {string} user 用户名
     * @param {string} password 密码
     * @returns {Promise}  服务器返回的登录请求的结果
     */
    static logon(user, password) {
        password = CryptoJS.MD5(password).toString();
        const data = { user, password };
        const route = "logon";
        return this._createLink(route, JSON.stringify(data));
    }

    /**
     * 向登录服务器发送注册请求
     * @param {string} user 用户名
     * @param {string} password 密码
     * @return {Promise}  服务器返回的注册请求的结果
     */
    static register(user, password) {
        password = CryptoJS.MD5(password).toString();
        const data = { user, password };
        const route = "register";
        return this._createLink(route, JSON.stringify(data));
    }

    /**
     * 向服务器获取当前账号下所有角色
     * @returns {Promise}  服务器返回的所有角色数据
     */
    static getActorList() {
        const token = this.token;
        const data = { token }
        const route = "actor";
        return this._createLink(route, JSON.stringify(data));
    }

    /**
     * 向服务器发送创建角色请求
     * @param {string} name 角色名字
     * @param {number} id 职业id
     * @returns {Promise}  服务器返回的所有角色数据
     */
    static createActor(name, id) {
        const token = this.token;
        const data = { token, name, id };
        const route = "creatActor";
        return this._createLink(route, JSON.stringify(data));
    }

    /**
     * 选择角色并向服务器发起开始游戏请求
     * @param {number} uid 选择进入游戏的角色uid
     */
    static startGame(uid) {
        const token = this.token;
        const data = { uid, token };
        const route = "authentication";
        const actorData = this._createLink(route, JSON.stringify(data));
        actorData.then((result) => {
            switch (result.code) {
                case 100:
                    this._ready = true;
                    const { identity } = result;
                    NetManager.showPing();
                    //从地图服务器获取交互信息
                    this.emit("map", "start", identity);
                    break;
                default:
                    SceneManager._scene._tipWindow.pop("寄");
                    break;
            }
        });
    }

    /**
     * 创建与地图交互服务器的socket连接
     */
    static createMapSocket() {
        this._mapSocket = io("http://101.34.11.85:6544");
        NetMap.createSocketEvent(this._mapSocket);
    }

    /**
     * 创建与数据战斗服务器的socket连接
     */
    static createDataSocket() {
        this._dataSocket = io("http://101.34.11.85:6545");
        NetData.createSocketEvent(this._dataSocket);
    }

    /**
     * 通过socket连接向服务器发送事件请求
     * @param {string} server 目标服务器名字
     * @param {string} event 事件
     * @param {string|object|number} data 数据
     */
    static emit(server, event, data) {
        let count = Date.now();
        const serverName = `_${server}Socket`;
        switch (server) {
            case "map":
                if (!this[serverName]) this.createMapSocket();
                break;
            case "data":
                if (!this[serverName]) this.createDataSocket();
                break;
            default:
                break;
        }
        this[serverName].emit(event, data, (response) => {
            this._ping = Date.now() - count;
            count = null;
        })
    }

    /**
     * net场所移动，取代rm原本该功能函数
     * @param {number} mapId 目标地图id
     * @param {number} mapX 目标地图x坐标
     * @param {number} mapY 目标地图y坐标
     */
    static transfer(mapId, mapX, mapY, d, fadeType) {
        if (!this._ready) return;
        if (mapId !== $gameMap.mapId()) NetManager.players = new Map();
        NetMap.reserveTransfer(mapId, mapX, mapY, d, fadeType);
    }

    /**
     * 获取对应uid的玩家基础信息，用于特殊情况某些玩家信息没有被正确获取到的情况
     * @param {number} uid 玩家uid
     */
    static getPlayerBaseInfo(uid) {
        this.emit("map", "get info of uid", uid);
    }

    /**
     * 获取对应uid的玩家基础信息，用于特殊情况某些玩家信息没有被正确获取到的情况
     * @param {number[]} uid 玩家uid数组
     */
    static getPtPlayerBaseInfo(uid) {
        this.emit("map", "get pt-info of uid", uid);
    }

    /**
     * 以自身为队长创建一个队伍，必须为未加入队伍的状态
     * @returns {Net_party}
     */
    static createParty() {
        if (this.party) return;
        const uid = this.actor.uid();
        this.party = new Net_Party();
        this.party._leader = uid;
        return this.party;
    }

    /**
     * 邀请目标玩家加入自己的队伍,只有队长才有权限邀请,如果自身没有队伍会自动创建队伍,会返回创建结果
     * @param {number} uid 目标玩家的uid 
     * @return {number} 请求结果code 801：无权限；802：队伍人数已满；803：成功；
     */
    static invitingTeams(uid) {
        const party = this.party ?? this.createParty();
        const myUid = this.actor.uid();
        if (party.leader() !== myUid) return 801;
        if (party.size() >= 5) return 802;
        this.emit("map", "sending party invite", uid);
        return 800;
    }

    /**
     * 同意当前收到的组队邀请
     */
    static partyAgree() {
        this.emit("map", "party agree");
    }

    /**
     * 拒绝当前收到的组队邀请
     */
    static partyRefuse() {
        this.emit("map", "party refuse");
    }

    /**
     * 离开队伍
     */
    static leaveParty() {
        if (!NetManager.party) return;
        console.log(this.party, SceneManager._scene._partyWindow);
        this.party = null;
        this.emit("map", "leave party");
        const partyWindow = SceneManager._scene._partyWindow;
        if (partyWindow) partyWindow.refresh();
    }

    /**
     * 事件结算
     * @param {number} eventId 事件id（服务端配置文件中的id）
     */
    static eventSettle(eventId) {
        this.emit("data", "event complete", eventId);
    }

    //移到ui里
    static createInput() {
        const input = document.createElement("input");
        input.style.position = "fixed";
        input.style.zIndex = "15";
        input.style.fontSize = "28px";
        // element.style.background = "grey";
        input.style.cursor = "default";
        input.addEventListener("touchstart", e => e.stopPropagation());
        input.addEventListener("keydown", function (e) {
            if (e.keyCode === 8) {
                e.stopPropagation();
            }
        })
        return input;
    }

    /**
     * 将fps显示开启，多次执行会按顺序切换状态：模式1 → 模式2 → 关闭 
     */
    static showPing() {
        Graphics._fpsCounter.switchMode();
    }

}

/**
 * 一些地图交互相关方法静态类
 * @class
 */
class NetMap {
    /**
     * 记录传送信息
     * @param {number} mapId 地图id
     * @param {number} x 地图x坐标
     * @param {number} y 地图y坐标
     * @param {number} d 传送后方向
     * @param {number} fadeType 淡入淡出
     */
    static reserveTransfer(mapId, x, y, d, fadeType) {
        this._newMapId = mapId;
        this._newMapX = x;
        this._newMapY = y;
        this._newDirection = d;
        this._fadeType = fadeType;
        NetManager.emit("map", "get map player", mapId);
    }

    /**
     * 获取传送请求目的地位置坐标，如果不存在则返回false
     * @returns {object} 地图坐标对象
     */
    static _getTransferLocation() {
        let data;
        if (this._newMapId > 0 && this._newMapX >= 0 && this._newMapY >= 0) {
            data = {
                "mapId": this._newMapId,
                "x": this._newMapX,
                "y": this._newMapY,
                "d": this._newDirection || 2,
                "fadeType": this._fadeType || 0
            };
            return data;
        } else return false;
    }

    /**
     * 清除传送信息，与rm原始$Game_Player上的方法类似
     */
    static _clearTransferInfo() {
        this._newMapId = null;
        this._newMapX = null;
        this._newMapY = null;
        this._newDirection = null;
        this._fadeType = null;
    }

    /**
     * 与服务器同步游戏开关、变量、独立开关
     * @param {object} data 云变量数据
     */
    static _syncVariables(data) {
        const { cloudSwitches, cloudVariables, cloudSelfSwitches } = data;
        for (const switch_ of Object.keys(cloudSwitches)) {
            $gameSwitches._data[switch_] = cloudSwitches[switch_];
        }
        for (const variable of Object.keys(cloudVariables)) {
            $gameVariables._data[variable] = cloudVariables[variable];
        }
        for (const selfSwitch of Object.keys(cloudSelfSwitches)) {
            $gameSelfSwitches._data[selfSwitch] = cloudSelfSwitches[selfSwitch];
        }
        $gameMap.requestRefresh();
    }

    /**
     * 创建socket事件
     * @param {io} socket 地图服务器连接socket实例
     */
    static createSocketEvent(socket) {
        //服务器重启导致的socket断线重连机制引发的错误，强制踢下线，此为暂时解决方案！！
        socket.on('err link', () => {
            socket.disconnect();
            NetManager._mapSocket = null;
            SceneManager.goto(Scene_Title);
        })

        //在线玩家人数刷新
        socket.on("player refresh", (data) => {
            NetManager._playerNum = data;
        })

        //提示信息
        socket.on("tip pop", (data) => {
            SceneManager._scene?._tipWindow.pop(data);
        })

        //获取角色信息
        socket.on("actor base info", (data) => {
            const { party, actor } = data;
            NetManager.actor.setNetBase(actor);
            const { mapId, x, y } = actor;
            NetManager.setStartingPoint(mapId, x, y);
            DataManager.setupNewGame();
            if (party) {
                NetManager.party = new Net_Party();
                NetManager.party.setup(party);
            }
        })

        //接收当前地图玩家数据
        socket.on("all players info", (data) => {
            for (const player of data) {
                const { uid } = player;
                const netCharacter = new Net_Character(player);
                NetManager.players.set(uid, netCharacter);
            }
            //其他玩家数据处理完毕后执行场所移动
            const { mapId, x, y, d, fadeType } = NetMap._getTransferLocation();
            $gamePlayer.reserveTransfer(mapId, x, y, d, fadeType);
            NetMap._clearTransferInfo();
        })

        //单独接收当前地图玩家数据
        socket.on("players info", (data) => {
            const { uid, mapId } = data;
            if (mapId === $gameMap.mapId() && !NetManager.players.has(uid)) {
                const netCharacter = new Net_Character(data);
                netCharacter.add();
            }
        })

        //单独接收队伍玩家数据
        socket.on("pt-players info", (data) => {
            if (Array.isArray(data)) {
                NetManager.party.include(data);
                return;
            }
        })

        //处理玩家进入地图
        socket.on('enter map', (data) => {
            const netCharacter = new Net_Character(data);
            netCharacter.add();
        })

        //处理玩家离开地图
        socket.on('leave map', (data) => {
            const { uid } = data;
            if (NetManager.players.has(uid)) {
                const netCharacter = NetManager.players.get(uid);
                netCharacter.remove();
            }
        })

        //接受其他玩家移动指令
        socket.on('move', (data) => {
            const { uid, mapId, x, y } = data;
            const netCharacter = NetManager.players.get(uid);
            if (mapId !== $gameMap.mapId()) return;
            if (!netCharacter) {
                NetManager.getPlayerBaseInfo(uid);
                return;
            }
            netCharacter.setNetPosition(x, y);
        })

        //玩家地图状态改变
        socket.on('player map state', (data) => {
            const { uid, state } = data;
            const netCharacter = NetManager.players.get(uid);
            if (!netCharacter) {
                console.log("玩家数据不存在");
                return;
            }
            netCharacter.setMapState(state);
        })

        //云变量信息
        socket.on("cloud variables info", (data) => {
            NetMap._syncVariables(data);
        })

        //云开关、变量
        socket.on('switch change', (data) => {
            const { key, value } = data;
            $gameSwitches.setValue(key, value);
        })
        socket.on('variable change', (data) => {
            const { key, value } = data;
            $gameVariables.setValue(key, value);
        })
        socket.on('selfSwitch change', (data) => {
            const { key, value } = data;
            $gameSelfSwitches.setValue(key, value);
        })

        //组队相关

        //收到组队邀请
        socket.on("party invite", (data) => {
            const { leaderName } = data;
            const text = `收到${leaderName}的组队邀请，是否接受？`;
            const window = SceneManager._scene?._confirmWindow;
            if (window) {
                window.setData("party", data);
                window.setText(text);
                window.setMode("party");
                window.show();
            }
        })

        //更新队伍
        socket.on("updata party", (data) => {
            const partyWindow = SceneManager._scene._partyWindow;
            const tipWindow = SceneManager._scene._tipWindow;
            const name = NetManager.actor.name();
            const party = NetManager.party;
            const { partyData, code, obj } = data;
            const text = $dataText[code].format(obj);
            if (tipWindow) {
                tipWindow.pop(text);
            }
            party.setup(partyData);
            //     console.log(data);

            //     switch (code) {
            //         case 132:
            //             const text = $dataText[]
            //             break;
            //         default:
            //             break;
            //     }
        })

        //队伍解散
        socket.on("party dissolve", () => {
            NetManager.party = null;
            const partyWindow = SceneManager._scene._partyWindow;
            const tipWindow = SceneManager._scene._mapNameWindow;
            if (partyWindow) partyWindow.refresh();
            if (tipWindow) tipWindow.pop("队伍已解散");

        })


    }
}

/**
 * 一些数据战斗相关方法静态类
 * @class
 */
class NetData {
    /**
     * 创建socket事件
     * @param {io} socket 数据服务器连接socket实例
     */
    static createSocketEvent(socket) {
        //获得奖励
        socket.on("gain bonus", (data) => {
            const list = Object.keys(data.bonus);
            for (const bonus of list) {
                switch (bonus) {
                    case "exp":
                        NetManager.actor.gainExp(data.bonus.exp)
                        break;
                    default:
                        break;
                }
            }
        })
    }
}